//////////////////////////////////////////////////////////////////////////////
//
// File:  buff.cc
//
// Description:
//   This is the implementation file for the buff class that manages a buffer.
//
// How it works:
//   This class is basically a buffer of data held by a unsigned char* data 
//   pointer.  Within this buffer lies possibly many packets.  Packets are
//   a chunk of data prepared in a fashion that complies with the bnet
//   protocol.  Packets contain a header consisting of a 0xFF (u8 byte),
//   packet ID ( u8 byte), and size of the packet (short int).  In this whole
//   thing, two 'size' values are manazed.  The 'unsigned short sz' value
//   is the size of the whole buffer and whatever packets it may contain.
//   The 'unsigned short packetSz' is the size of the current packet
//   we are on/dealing with.  When data is extracted from the buffer,
//   both sizes are decremented by the appropriate amount.
//
// Notes:
//   This class uses an externed ofstream *debug.  It must be set up properly
//   beforehand!
//
//


#include <unistd.h>
#include "buff.h"
#include "types.h"
extern ofstream *debug;


//////////////////////////////////////////////////////////////////////////////
//
// Function:  string print_hex_form(u8 x)
//
// Description:
//   This function prints the u8 in string form and returns it.
//
//
string print_hex_form(u8 x) {
  string rv = "0x";

  if((x/16) == 10)
    rv += "A";
  else if((x/16) == 11)
    rv += "B";
  else if((x/16) == 12)
    rv += "C";
  else if((x/16) == 13)
    rv += "D";
  else if((x/16) == 14)
    rv += "E";
  else if((x/16) == 15)
    rv += "F";
  else
    rv += char(48 + (x/16));

  if((x%16) == 10)
    rv += "A";
  else if((x%16) == 11)
    rv += "B";
  else if((x%16) == 12)
    rv += "C";
  else if((x%16) == 13)
    rv += "D";
  else if((x%16) == 14)
    rv += "E";
  else if((x%16) == 15)
    rv += "F";
  else
    rv += char(48 + (x%16));
  
  rv += " ";
  return rv;
}  // end of    string print_hex_form(u8 x)


//////////////////////////////////////////////////////////////////////////////
//
// Function:  string print_hex_form(u32 x)
//
// Description:
//   This function prints the u32 in string form and returns it.
//
//
string print_hex_form(u32 x) {
  string rv = "";
  for(unsigned int i = 0; i < sizeof(u32); i++) {
    rv += print_hex_form( *(((u8*)&x) +i) );
  }
  return rv;
}  // end of    string print_hex_form(u32 x)



//////////////////////////////////////////////////////////////////////////////
//  
// Function:  buff::buff()
//
// Description:
//   Default constructor for the buff class.  Initializes vars.
//
buff::buff() {
  data = NULL;
  sz = 0;
  startByte = (u8)0xFF;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  buff::~buff()
//
// Description:
//  Destructor for the buff class.  Just clears the buffer. (deallocates).
//
buff::~buff() {
  // Deallocate mem.
  clear();
}



//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::add(const void* toAdd, int bytes)
//
// Description:
//   This function adds 'bytes' bytes of data to the buffer from the data
//   pointed to by toAdd.
//
// Notes:
//   Don't be stupid.  No null pointers at 'toAdd' plz.
//
bool buff::add(const void* toAdd, int bytes) {
  
  // It aint crash'in on my watch...
  if(toAdd == NULL) {
    *debug << "WARNING:  Tried to add data at void *toAdd to the buffer, "
	   << "but toAdd == NULL.";
    return false;
  }
  if((sz == 0) && (data == NULL)) {
    // Nothing in the buffer right now, so allocate toAdd # of bytes for toAdd.
    data = new unsigned char[bytes];
    memcpy(data, toAdd, bytes);
    // Update the size of the buffer.
    sz = bytes;
  }
  else {
    // Buffer has something in it.  Append toAdd to it data & update size.
    
    unsigned char* oldData = data;
    data = new unsigned char[sz + bytes];
    if(data == NULL) {
      *debug << "ERROR:  Couldn't allocate mem for an addition to buff" << endl
	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }
    // Copy the oldData to the data arr.
    memcpy(data, oldData, sz);
    // Copy the toAdd data to the data arr.
    memcpy(data+sz, toAdd, bytes);
    // Update size;
    sz += bytes;
    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  return true;
}  //  end of    bool buff::add(const void* toAdd, int bytes)


//////////////////////////////////////////////////////////////////////////////
//
// Function:  void buff::clear()
//
// Description:
//   This function clears (deallocates) the buffer.
//
void buff::clear() {
  // Deallocate memory 
  if (data != NULL) {
    if(sz == 0) {
      // wtf?  Error & Quit.
      *debug << "ERROR:  buff size was 0 but data ptr was not NULL" << endl
	    << "        "    << __FILE__ << "(" << __LINE__ << ")" << endl;
      exit(1);
    }
    delete [] data;
    data = NULL;
  }
  sz = 0;
  packetID = 0;
  packetSz = 0;
}



//////////////////////////////////////////////////////////////////////////////
//
// Function:  short buff::size()
// 
// Description:
//   Accessor function for the size of the buffer.
//
short buff::size() {
  return sz;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  void* buff::getPtr()
//
// Description:
//   Returns a void pointer to where the data is pointed to.  There is by no
//   guarentee it is not pointing to NULL and to how much data it is pointing
//   to.  You have to check the buffer size to know!
//
void* buff::getPtr() {
  return data;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  void buff::setPacketID(u8 newID)
// 
// Description:
//   Sets the packetID.
// 
void buff::setPacketID(u8 newID) {
  packetID = newID;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  u8 buff::getPacketID()
//
// Description:
//   Accessor function that returns the packetID.
//
u8 buff::getPacketID() {
  return packetID;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  void buff::setPacketSz(unsigned short newSz)
//  
// Description:
//   This function sets the current packet size of the packet it is currently
//   working on/at in the total buffer.  If this doesn't make sense, read
//   the comments at the top of this file.
//
void buff::setPacketSz(unsigned short newSz) {
  packetSz = newSz;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:   unsigned short buff::getPacketSz()
// 
// Description:
//   Accessor function that returns the current packet size in the buffer.
//
unsigned short buff::getPacketSz() {
  return packetSz;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::package()
//
// Description:
//   This function prepares the buffer to be sent.  This prepends
//   the 0xFF initial byte, the packetID, and the size.
//
bool buff::package() {
  short currPos = 0;

  // Prepend 0xFF, packageID, packet size to the buffer.
  short futureSize = 1 + 1 + sizeof(short) + sz;
  // This is the side after the header has been added.
  unsigned char* oldData = data;
  data = new unsigned char[sz + 1 + 1 + sizeof(short)];
  if(data == NULL) {
    *debug << "ERROR:  Couldn't allocate mem for a package buff" << endl
	   << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  memcpy(data, &startByte, 1);
  currPos += 1;
  memcpy(data + currPos, &packetID, 1);
  currPos += 1;

  // We use futureSize here because the sz value will not have been updated
  //   untill after the memcpys.
  memcpy(data + currPos, &futureSize, sizeof(short));
  currPos += sizeof(short);
  
  // If oldData is NULL then sz == 0.  We don't want to do a memcpy then.
  if(oldData != NULL) {
    // Copy the oldData to the data arr.
    memcpy(data + currPos, oldData, sz);
    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  // Update size;
  sz += currPos;

  *debug << "DEBUG:  Package ID " << print_hex_form(packetID)
	 << " packaged with size " << sz << "." << endl;
  return true;
}  // end of    bool buff::package()



//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::strip()
//
// Description:  
//   This function strips the header info off and leaves just the data.
//   The header info is then stored in the appropriate member variables.
//   I use this function for the recv buffer only.
//   
//   Header info includes:  
//  - 0xFF   Initial 1 byte.
//  - 0x__   Packet ID 1 byte.
//  - 0x____ Packet size 2 bytes.
//
// 

bool buff::strip() {
  if(sz == 0 || data == NULL) {
    *debug << "ERROR:  Attempt to strip off the header to a null or 0 sized "
	   << "buffer. " << endl;
    return false;
  }
  //  *debug << "DEBUG: sz == " << sz << endl;
  //  print_buffer();
  //  *debug << "DEBUG:  finished printing buffer" << endl;
  //  extract(startByte);
  //  *debug << "DEBUG:  extracted startByte" << endl;
  //  extract(packetID);
  //  *debug << "DEBUG:  extracted packetID" << endl;
  //  extract(packetSz);
  // Manually set the remaining packet size because we just extracted it.



  *debug << "DEBUG:  extracted packetSz" << endl;

  if(!extract(startByte) || !extract(packetID) || !extract(packetSz)) {
    *debug << "ERROR:  strip of header failed" << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  
  // Update the packetSz to represent the packet after the strip.
  packetSz -= (sizeof(u8) + sizeof(u8) + sizeof(unsigned short));

  if(startByte != 0xFF) {
    // Bad news.  Either we're off, or bnet broke protocol.
    *debug << "WARNING:  First byte in packet recv is not 0xFF. " << endl;
  }
  *debug << "DEBUG:  stripped.  dataupdate: "  << endl
	 << "  startByte: " << print_hex_form(startByte) << endl
	 << "  packetID:  " << print_hex_form(packetID) << endl
	 << "  size left  " << sz << endl
	 << "  packetSz   " << packetSz << endl;
  return true;
} // end of   bool buff::strip()



//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::extract(u8 &stuff)
// 
// Descriptions:
//   Removes a u8 from the front of the buffer and adjusts the sizes
//   accordingly.
//
// Return value:
//   T/F for the success of the operation.
//
bool buff::extract(u8 &stuff) {
  u8 newStuff;
  if(sz > sizeof(u8)) {
    unsigned char* oldData = data;
    data = new unsigned char[sz - sizeof(u8)];
    if(data == NULL) {
      *debug << "ERROR:  Couldn't allocate mem for a buff extract(u8)" << endl
	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u8));
    packetSz -= sizeof(u8);
    sz -= sizeof(u8);
    memcpy(data, oldData + sizeof(u8), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u8)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u8));
    stuff = newStuff;
    clear();
  }
  else {
    *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u8):  sz == " << sz << endl;
  return true;
}  // end of    bool buff::extract(u8 &stuff)


//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::extract(u16 &stuff)
// 
// Descriptions:
//   Removes a u16 from the front of the buffer and adjusts the sizes
//   accordingly.
//
// Return value:
//   T/F for the success of the operation.
//
bool buff::extract(u16 &stuff) {
  u16 newStuff;
  if(sz > sizeof(u16)) {
    unsigned char* oldData = data;
    data = new unsigned char[sz - sizeof(u16)];
    if(data == NULL) {
      *debug << "ERROR:  Couldn't allocate mem for a buff extract(u16)" << endl
	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u16));
 
    packetSz -= sizeof(u16);
    sz -= sizeof(u16);

    memcpy(data, oldData + sizeof(u16), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u16)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u16));
    stuff = newStuff;
    clear();
  }
  else {
    *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u16):  sz == " << sz << endl;
  return true;
} // end of     bool buff::extract(u16 &stuff)


//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::extract(u32 &stuff)
// 
// Descriptions:
//   Removes a u32 from the front of the buffer and adjusts the sizes
//   accordingly.
//
// Return value:
//   T/F for the success of the operation.
//
bool buff::extract(u32 &stuff) {
  u32 newStuff;
  if(sz > sizeof(u32)) {
    unsigned char* oldData = data;
    data = new unsigned char[sz - sizeof(u32)];
    if(data == NULL) {
      *debug << "ERROR:  Couldn't allocate mem for a buff extract(u32)" << endl
	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u32));
    packetSz -= sizeof(u32);
    sz -= sizeof(u32);
    memcpy(data, oldData + sizeof(u32), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u32)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u32));
    stuff = newStuff;
    clear();
  }
  else {
    *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u32):  sz == " << sz << endl;
  return true;
} //  end of   bool buff::extract(u32 &stuff)


//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::extract(u64 &stuff)
// 
// Descriptions:
//   Removes a u64 from the front of the buffer and adjusts the sizes
//   accordingly.
//
// Return value:
//   T/F for the success of the operation.
//
//
bool buff::extract(u64 &stuff) {
  u64 newStuff;
  if(sz > sizeof(u64)) {
    unsigned char* oldData = data;
    data = new unsigned char[sz - sizeof(u64)];
    if(data == NULL) {
      *debug << "ERROR:  Couldn't allocate mem for a buff extract(u64)" << endl
	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u64));
    packetSz -= sizeof(u64);
    sz -= sizeof(u64);
    memcpy(data, oldData + sizeof(u64), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u64)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u64));
    stuff = newStuff;
    clear();
  }
  else {
    *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u64):  sz == " << sz << endl;
  return true;
} // end of   bool buff::extract(u64 &stuff)


//////////////////////////////////////////////////////////////////////////////
//
// Function:   bool buff::extract(char *stuff)
//
// Description:
//   This function extracts the rest of the buffer and sticks it in
//   char *stuff.  
//
// Precondition:  *stuff must be allcoated or have space ahead of time!!!!!!!!
//                SEGFAULT is certain otherwise!
//
// Algorithm:
//   I am doing this by strcpy-ing copying a string into *stuff.  This is done
//   by repeated calls to the extract(u8) function.
//
// Notes:
//   I suppose this is why programming in C style is bad.  
//   *stuff could point to an area of memory either not allocated 
//   or not have enough space.
//
//   8/9/2004:
//     I think I have found the problem to why the program sometimes crashes
//     after a few days worth of running.  It will sometimes recieve a
//     SID_CHATEVENT, and then when we do an extract on it, the text field
//     comes out all bad.  Dunno how, but anyways, there are a lot of 0xFF's 
//     in it, which is obviously the UTF-8.  And since most of my stuff is
//     for just the ascii part of it, the 0xFF REALLY should be just 
//     characters that won't display right.  I'm not sure what I should do
//     at this point, but for now I will just ignore all characters that are
//     out of the ascii range.  Hopefully this won't break anything.  If it
//     does... I know where to look.   *_*.  Modifying:  conGuy/buff.cc,
//     function bool buff::extract(char *stuff).

bool buff::extract(char *stuff) {

  char extracted[1024];
  u8 aChar;
  int i;

  // Although inefficient, these repetitve extract calls will update
  //   sz and packetSz and fix the buffer for us.  Reuse of code, yay.

  if(sz == 0) {
    *debug << "ERROR:  Tried to extract(char*) from an empty buffer." << endl
	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;

    return false;
  }
  // Revision for 1.13
  //   Go through the extract, character by character untill it either fails an
  //   extract, or the buffer ended with '\0' (NULL).  We are checking here
  //   for any out of bounds characters (since I'm only doing ASCII and the
  //   protocol is for it to be in UTF-8).  Any out of bounds character will
  //   cause the null character to be appended to the 'extracted' c-string
  //   ('extracted' string is now totally complete for our purposes),
  //   and the rest of the extracts to complete and get thrown away untill the 
  //   final '\0', which is also thrown away too.  

  // old code (that prolly doesn't work):  replaced by the below.
//   for(i = 0; ((extract(aChar)) && (aChar != '\0')); i++) {
//     // Copy the character over.
//     memcpy((extracted+i), &aChar, sizeof(char));
//     if(sz == 0) {
//       *debug << "ERROR:  Unable to extract(*char).  Buffer is empty and " 
// 	     << endl << "         no '\\0' was found." << endl
// 	     << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
//       return false;
//     }
//   }
//   // Copy over the null too.
//   memcpy((extracted+i), &aChar, sizeof(char));

  // Newer code reflecting 1.13 changes.
  for(i = 0; extract(aChar); i++) {
    // If the character is in range, then copy it over.
    if((aChar == 0) || 
       ((aChar >= 32) && (aChar <= 126))) {
      // The char is in range.
      // Copy the character over.
      memcpy((extracted+i), &aChar, sizeof(char));
    }
    else {
      // The char is out of range.
      // Let's put a NULL at this spot (just in case), and do another 
      // extract and try this spot in the 'extracted' c-string
      // again.  (by decrementing i).
      *debug << "DEBUG:  AH HA!  We finally got a char out of range.  Just "
	     << "a note." << endl;
      char nullChar = '\0';
      memcpy((extracted+i), &nullChar, sizeof(char));
      i--;
    }
      
    if(sz == 0 && aChar != '\0') {
      *debug << "ERROR:  Unable to extract(*char).  Buffer is empty and " 
	     << endl << "         no '\\0' was found." << endl
	     << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Break out of the FOR loop if the last char extracted was a NULL ('\0')
    if(aChar == '\0')
      break;
  }


  
  // Copy extracted to the stuff that is getting returned.
  strcpy(stuff, extracted);
  //  *debug << "DEBUG: extract(char*):  sz == " << sz << endl;
  return true;
}  // end of   bool buff::extract(char *stuff)


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool buff::extract(string &stuff)
//
// Description:
//   This function extracts the buffer and puts it into the string 'stuff'.
//   
// Precondition:
//   The buffer must contain less than 1024 bytes.  (Which should be true
//   in all our cases.)
//
bool buff::extract(string &stuff) {
  char extracted[1024];
  if(sz > 1024) {
    *debug << "ERROR:  extract(string).  buff sz > 1024."
	   << endl << "          " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    return false;
  }
  if(!extract(extracted)) {
    *debug << "ERROR:  extract(string) call to extract(char*) failed."
	   << endl << "          " << __FILE__ << "(" << __LINE__ << ")" 
	   << endl;
    return false;
  }
  stuff = (string)extracted;
  return true;
}  // end of    bool buff::extract(string &stuff)



//////////////////////////////////////////////////////////////////////////////
//
// Function:   void buff::print_buffer()
//
// Description:
//   This function prints the buffer in it's current state/contents.
//
//
void buff::print_buffer() {
   for(int i=0; i < sz; i++) {
     *debug << print_hex_form(*(data+i));
   }
   *debug << endl;
   (*debug).flush();
}



// End of buff.cc
